package com.crscd.jdbc.senior.dao;

import com.crscd.jdbc.senior.util.JdbcUtilv2;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Created with IntelliJ IDEA. 将共性的方法封装在BaseDao中
 *
 * @description:
 * @author: liuziyang
 * @since: 10/23/25 11:46 AM
 * @modifiedBy:
 * @version: 1.0
 */
public abstract class BaseDao {
  /**
   * Execute update int.通用的增删改
   *
   * @param sql the sql
   * @param params the params
   * @return the int
   */
  public int executeUpdate(String sql, Object... params) throws SQLException {
    // 1. 通过JdbcUtilv2获取数据库连接
    final Connection connection = JdbcUtilv2.getConnection();

    // 2. 预编译sql语句
    final PreparedStatement statement = connection.prepareStatement(sql);

    // 3. 为占位符赋值，执行sql，接受返回结果
    if (Objects.nonNull(params) && params.length > 0) {
      for (int i = 0; i < params.length; i++) {
        // 占位符是从1开始的
        statement.setObject(i + 1, params[i]);
      }
    }
    final int result = statement.executeUpdate();
    // 5. 释放资源
    statement.close();
    if (connection.getAutoCommit()) {
      JdbcUtilv2.release();
    }

    // 4. 处理结果
    return result;
  }

  /**
   * 通用的查询，多行多列，单行多列，单行单列 - 多行多列：List<Employee> - 单行多列：Employee - 单行单列，封装的是一个结果。double、integer 封装过程
   * 1. 返回的类型：泛型，类型不确定，调用者知道，调用时，将此次查询的结果类型告知BaseDao就可以了 2. 返回的结果，通用List，可以存储多个结果，也可以存储一个结果get（0） 3.
   * 结果的封装，反射，要求调用者告知BaseDao要封装对象的类对象。Class
   */
  public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params)
      throws SQLException,
          NoSuchMethodException,
          InvocationTargetException,
          InstantiationException,
          IllegalAccessException,
          NoSuchFieldException {
    // 获取连接
    Connection connection = JdbcUtilv2.getConnection();
    // 预编译sql语句
    final PreparedStatement statement = connection.prepareStatement(sql);
    // 设置占位符的值
    if (Objects.nonNull(params) && params.length > 0) {
      for (int i = 0; i < params.length; i++) {
        statement.setObject(i + 1, params[i]);
      }
    }

    // 执行sql，并接收返回结果
    final ResultSet resultSet = statement.executeQuery();
    // 获取结果集中元数据对象，该对象包含了列的数量和每个列的名称
    final ResultSetMetaData metaData = resultSet.getMetaData();

    List<T> list = new ArrayList<>();
    // 处理结果
    while (resultSet.next()) {
      T t = clazz.getDeclaredConstructor().newInstance();
      final int columnCount = metaData.getColumnCount();
      for (int i = 1; i <= columnCount; i++) {
        // 通过下标获取列的值
        final Object value = resultSet.getObject(i);
        // 获取到列的value值，这个值就是t这个对象中的某一个属性
        // 获取当前拿到的列的名字 = 对象的属性值
        final String fieldName = metaData.getColumnLabel(i);
        // 通过类对象和fieldName获取要封装对象的属性
        final Field field = clazz.getDeclaredField(fieldName);
        // 突破封装的private
        field.setAccessible(true);
        // 处理BigDecimal到Double的转换问题
        if (value instanceof java.math.BigDecimal && field.getType().equals(Double.class)) {
          field.set(t, ((java.math.BigDecimal) value).doubleValue());
        } else {
          field.set(t, value);
        }
      }
      list.add(t);
    }

    resultSet.close();
    statement.close();
    if (connection.getAutoCommit()) {
      JdbcUtilv2.release();
    }

    return list;
  }

  /** 通用查询： 在上面查询的集合结果中获取第一个结果，简化了单行单列的获取和单行多列的获取 */
  public <T> T executeQueryBean(Class<T> clazz, String sql, Object... params)
      throws SQLException,
          NoSuchFieldException,
          InvocationTargetException,
          NoSuchMethodException,
          InstantiationException,
          IllegalAccessException {
    final List<T> list = this.executeQuery(clazz, sql, params);
    if (Objects.isNull(list) || list.isEmpty()) {
      return null;
    }
    return list.get(0);
  }
}
